//------------------------------------------------------------------------------
// SolutionItem.cpp
//------------------------------------------------------------------------------
// File provided for Microsoft Shared Source.
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 
// PARTICULAR PURPOSE.
//------------------------------------------------------------------------------
//
// Abstraction classes over the EnvDTE::ProjectItem objects
//
//------------------------------------------------------------------------------

#include "pch.h"

//------------------------------------------------------------------------------
// CSolutionItem
//------------------------------------------------------------------------------

class ATL_NO_VTABLE CSolutionItem :
    public CComObjectRootEx<CComSingleThreadModel>,
    public IObjectWithSiteImpl<CSolutionItem>,
    public ISolutionItem
{
public:
    CSolutionItem() : _iIconIndex(-1) {}
    static HRESULT s_CreateInstance(__in ProjectItem *pvsProjectItem, __in ISolutionItemTypeCache *psitc, REFIID riid, __deref_out void **ppv);

    BEGIN_COM_MAP(CSolutionItem)
        COM_INTERFACE_ENTRY(IObjectWithSite)
        COM_INTERFACE_ENTRY(ISolutionItem)
    END_COM_MAP()

    DECLARE_PROTECT_FINAL_CONSTRUCT()

    // ISolutionItem
    IFACEMETHODIMP GetIconIndex(__out int *piIndex);
    IFACEMETHODIMP GetName(__out_ecount(cch) PWSTR pszName, int cch);
    IFACEMETHODIMP GetFullPath(__out_ecount(cch) PWSTR pszName, int cch);
    IFACEMETHODIMP GetPath(__out_ecount(cch) PWSTR pszPath, int cch);
    IFACEMETHODIMP GetSize(__out LARGE_INTEGER *pcb);
    IFACEMETHODIMP GetModified(__out FILETIME *pft);
    IFACEMETHODIMP GetCanonicalType(__out_ecount(cch) PWSTR pszCanonicalType, int cch);
    IFACEMETHODIMP GetItem(REFIID riid, __deref_out void **ppv);

private:
    HRESULT _Initialize(__in ProjectItem *pvsProjectItem, __in ISolutionItemTypeCache *psitc);
    HRESULT _EnsureFreshFileAttributes();

private:
    int _iIconIndex;
    CString _strFullPath;
    CString _strName;
    CString _strPath;
    CString _strCanonicalType;

    WIN32_FILE_ATTRIBUTE_DATA _fileAttributes;
    DWORD _dwTickCountFileAttributes;
    
    CComPtr<ProjectItem> _spvsProjectItem;
};

STDAPI CreateSolutionItem(__in ProjectItem *pvsProjectItem, __in ISolutionItemTypeCache *psitc, REFIID riid, __deref_out void **ppv)
{
    return CSolutionItem::s_CreateInstance(pvsProjectItem, psitc, riid, ppv);
}

HRESULT CSolutionItem::_Initialize(__in ProjectItem *pvsProjectItem, __in ISolutionItemTypeCache *psitc)
{
    _dwTickCountFileAttributes = 0;

    CComBSTR sbstrFile;
    HRESULT hr = pvsProjectItem->get_FileNames(0, &sbstrFile);
    if (SUCCEEDED(hr))
    {
        try
        {
            _strFullPath = sbstrFile;

            PWSTR pszFileName = PathFindFileName(sbstrFile);
            hr = pszFileName ? S_OK : E_INVALIDARG;
            if (SUCCEEDED(hr))
            {
                _strName = pszFileName;

                WCHAR szPath[MAX_PATH];
                hr = StringCchCopy(szPath, ARRAYSIZE(szPath), sbstrFile);
                if (SUCCEEDED(hr))
                {
                    PathRemoveFileSpec(szPath);
                    _strPath = szPath;

                    PWSTR pszExtension = PathFindExtension(pszFileName);
                    if (pszExtension)
                    {
                        _strCanonicalType = pszExtension;
                        TYPECACHEINFO tci;
                        hr = psitc->GetTypeInfo(_strCanonicalType, &tci);
                        if (SUCCEEDED(hr))
                        {
                            _iIconIndex = tci.iIconIndex;
                        }
                    }
                }
            }
        }
        catch (CAtlException &e)
        {
            hr = e.m_hr;
        }

        if (SUCCEEDED(hr))
        {
            _spvsProjectItem = pvsProjectItem;
        }
    }
    return hr;
}

HRESULT CSolutionItem::s_CreateInstance(__in ProjectItem *pvsProjectItem, __in ISolutionItemTypeCache *psitc, REFIID riid, __deref_out void **ppv)
{
    *ppv = NULL;
    CComObjectWithRef<CSolutionItem> *pspSolutionItem;
    HRESULT hr = CComObjectWithRef<CSolutionItem>::CreateInstance(&pspSolutionItem);
    if (SUCCEEDED(hr))
    {
        hr = pspSolutionItem->_Initialize(pvsProjectItem, psitc);
        if (SUCCEEDED(hr))
        {
            hr = pspSolutionItem->QueryInterface(riid, ppv);
        }
        pspSolutionItem->Release();
    }
    return hr;
}

// ISolutionItem
STDMETHODIMP CSolutionItem::GetIconIndex(__out int *piIndex)
{
    *piIndex = _iIconIndex;
    return S_OK;
}

STDMETHODIMP CSolutionItem::GetName(__out_ecount(cch) PWSTR pszName, int cch)
{
    return StringCchCopy(pszName, cch, _strName);
}

STDMETHODIMP CSolutionItem::GetFullPath(__out_ecount(cch) PWSTR pszPath, int cch)
{
    return StringCchCopy(pszPath, cch, _strFullPath);
}

STDMETHODIMP CSolutionItem::GetPath(__out_ecount(cch) PWSTR pszPath, int cch)
{
    return StringCchCopy(pszPath, cch, _strPath);
}

STDMETHODIMP CSolutionItem::GetItem(REFIID riid, __deref_out void **ppv)
{
    return _spvsProjectItem->QueryInterface(riid, ppv);
}

HRESULT CSolutionItem::_EnsureFreshFileAttributes()
{
    HRESULT hr = S_OK;

    DWORD dwTickCount = GetTickCount();
    if (dwTickCount - _dwTickCountFileAttributes > 1000)
    {
        hr = GetFileAttributesEx(_strFullPath, GetFileExInfoStandard, &_fileAttributes) ? S_OK : AtlHresultFromLastError();
        if (SUCCEEDED(hr))
        {
            _dwTickCountFileAttributes = dwTickCount;
        }
    }
    return hr;
}

STDMETHODIMP CSolutionItem::GetSize(__out LARGE_INTEGER *pcb)
{
    HRESULT hr = _EnsureFreshFileAttributes();
    if (SUCCEEDED(hr))
    {
        pcb->HighPart = _fileAttributes.nFileSizeHigh;
        pcb->LowPart = _fileAttributes.nFileSizeLow;
    }
    else
    {
        pcb->QuadPart = 0;
    }
    return hr;
}

STDMETHODIMP CSolutionItem::GetModified(__out FILETIME *pft)
{
    HRESULT hr = _EnsureFreshFileAttributes();
    if (SUCCEEDED(hr))
    {
        *pft = _fileAttributes.ftLastWriteTime;
    }
    else
    {
        *pft = FILETIME();
    }
    return hr;
}

STDMETHODIMP CSolutionItem::GetCanonicalType(__out_ecount(cch) PWSTR pszCanonicalType, int cch)
{
    return StringCchCopy(pszCanonicalType, cch, _strCanonicalType);
}

//------------------------------------------------------------------------------
// CSolutionItemGroup
//------------------------------------------------------------------------------

class ATL_NO_VTABLE CSolutionItemGroup :
    public CComObjectRootEx<CComSingleThreadModel>,
    public ISolutionItemGroup
{
public:
    static HRESULT s_CreateInstance(PCWSTR pszName, __in IUnknownArray *pua, REFIID riid, __deref_out void **ppv);

    BEGIN_COM_MAP(CSolutionItemGroup)
        COM_INTERFACE_ENTRY(ISolutionItemGroup)
    END_COM_MAP()

    DECLARE_PROTECT_FINAL_CONSTRUCT()

    // ISolutionItemGroup
    IFACEMETHODIMP GetName(__out_ecount(cch) PWSTR pszName, int cch);
    IFACEMETHODIMP GetItems(REFIID riid, __deref_out void **ppv);

private:
    HRESULT _Initialize(PCWSTR pszName, __in IUnknownArray *pua);

private:
    CString _strName;
    CComPtr<IUnknownArray> _spItems;
};

STDAPI CreateSolutionItemGroup(PCWSTR pszName, __in IUnknownArray *pua, REFIID riid, __deref_out void **ppv)
{
    return CSolutionItemGroup::s_CreateInstance(pszName, pua, riid, ppv);
}

HRESULT CSolutionItemGroup::_Initialize(PCWSTR pszName, __in IUnknownArray *pua)
{
    HRESULT hr = S_OK;
    try
    {
        _strName = pszName;
    }
    catch (CAtlException &e)
    {
        hr = e.m_hr;
    }

    if (SUCCEEDED(hr))
    {
        _spItems = pua;
    }
    return hr;
}

HRESULT CSolutionItemGroup::s_CreateInstance(PCWSTR pszName, __in IUnknownArray *pua, REFIID riid, __deref_out void **ppv)
{
    *ppv = NULL;
    CComObjectWithRef<CSolutionItemGroup> *pspsig;
    HRESULT hr = CComObjectWithRef<CSolutionItemGroup>::CreateInstance(&pspsig);
    if (SUCCEEDED(hr))
    {
        hr = pspsig->_Initialize(pszName, pua);
        if (SUCCEEDED(hr))
        {
            hr = pspsig->QueryInterface(riid, ppv);
        }
        pspsig->Release();
    }
    return hr;
}

// ISolutionItemGroup
STDMETHODIMP CSolutionItemGroup::GetName(__out_ecount(cch) PWSTR pszName, int cch)
{
    return StringCchCopy(pszName, cch, _strName);
}

STDMETHODIMP CSolutionItemGroup::GetItems(REFIID riid, __deref_out void **ppv)
{
    return _spItems->QueryInterface(riid, ppv);
}
